package com.yichuang.wuziqi.player;

import android.graphics.Point;
import android.support.annotation.Nullable;
import android.util.Log;

import com.alibaba.fastjson.JSONObject;
import com.yichuang.wuziqi.domain.Data;
import com.yichuang.wuziqi.tool.AIAlgorithm1;
import com.yichuang.wuziqi.tool.JsonTools;

import java.util.ArrayList;

/**
 * Created by xcm on 17-11-27.
 * 本地简单的AI
 */

public class LocalAI extends AI {

    private static final String TAG = "xcm";

    private ArrayList<Point> mMyPositions = new ArrayList<>();
    private ArrayList<Point> mOppositePositions = new ArrayList<>();

    private int[] myWin;
    private int[] oppositeWin;

    private int winCount = 0;

    //第一、二维表示该种赢法的5个落子位置，第三维表示第几种赢法，1表示该种赢法还有效
    private int[][][] allWins;

    public LocalAI(int boardLine, int level) {
        super(boardLine, level);
        this.boardLine = boardLine;
        if (level == AI.LEVEL_EASY) {
            initAI();
        }
    }

    public LocalAI(int side, int boardLine, int level) {
        super(side, boardLine, level);
        this.boardLine = boardLine;
        if (level == AI.LEVEL_EASY) {
            initAI();
        }
    }

    private void reset() {
        mMyPositions.clear();
        mOppositePositions.clear();
        for (int i = 0; i < winCount; i++) {
            myWin[i] = 0;
            oppositeWin[i] = 0;
        }
    }

    private void initAI() {
        winCount = 4 * (boardLine - 2) * (boardLine - 4);
        allWins = new int[boardLine][boardLine][winCount];
        int count = 0;
        //横向赢法统计
        for (int i = 0; i < boardLine; i++) {
            for (int j = 0; j < boardLine - 4; j++) {
                for (int k = 0; k < 5; k++) {
                    allWins[i][j + k][count] = 1;
                }
                count++;
            }
        }

        //纵向赢法统计
        for (int j = 0; j < boardLine; j++) {
            for (int i = 0; i < boardLine - 4; i++) {
                for (int k = 0; k < 5; k++) {
                    allWins[i + k][j][count] = 1;
                }
                count++;
            }
        }

        //左上右下赢法统计
        for (int i = 0; i < boardLine - 4; i++) {
            for (int j = 0; j < boardLine - 4; j++) {
                for (int k = 0; k < 5; k++) {
                    allWins[i + k][j + k][count] = 1;
                }
                count++;
            }
        }

        //左下右上统计
        for (int i = 0; i < boardLine - 4; i++) {
            for (int j = boardLine - 1; j >= 4; j--) {
                for (int k = 0; k < 5; k++) {
                    allWins[i + k][j - k][count] = 1;
                }
                count++;
            }
        }

        Log.d("xcm", "all win count=" + count);
        myWin = new int[winCount];
        oppositeWin = new int[winCount];
    }

    public Point algorithm1() {

        int myScore[][] = new int[boardLine][boardLine];
        int oppositeScore[][] = new int[boardLine][boardLine];
        int max = 0;
        int u = 0, v = 0;

        for (int i = 0; i < boardLine; i++) {
            for (int j = 0; j < boardLine; j++) {
                Point p = new Point(i, j);
                if (!mMyPositions.contains(p) && !mOppositePositions.contains(p)) {
                    //双方都没有在该位置下子，有效位置
                    for (int k = 0; k < winCount; k++) {
                        //遍历所有赢法
                        if (allWins[i][j][k] == 1) {
                            if (myWin[k] == 1) {
                                myScore[i][j] += 2;
                            } else if (myWin[k] == 2) {
                                myScore[i][j] += 5;
                            } else if (myWin[k] == 3) {
                                myScore[i][j] += 11;
                            } else if (myWin[k] == 4) {
                                myScore[i][j] += 100;
                            }

                            if (oppositeWin[k] == 1) {
                                oppositeScore[i][j] += 1;
                            } else if (oppositeWin[k] == 2) {
                                oppositeScore[i][j] += 4;
                            } else if (oppositeWin[k] == 3) {
                                oppositeScore[i][j] += 10;
                            } else if (oppositeWin[k] == 4) {
                                oppositeScore[i][j] += 50;
                            }
                        }
                    }
                }

                if (myScore[i][j] > max) {
                    max = myScore[i][j];
                    u = i;
                    v = j;
                } else if (myScore[i][j] == max) {
                    if (oppositeScore[i][j] > oppositeScore[u][v]) {
                        u = i;
                        v = j;
                    }
                }

                if (oppositeScore[i][j] > max) {
                    max = oppositeScore[i][j];
                    u = i;
                    v = j;
                } else if (oppositeScore[i][j] == max) {
                    if (myScore[i][j] > myScore[u][v]) {
                        u = i;
                        v = j;
                    }
                }
            }
        }
        Point p = new Point(u, v);
        mMyPositions.add(p);

        for (int i = 0; i < winCount; i++) {
            if (allWins[p.x][p.y][i] == 1) {
                myWin[i]++;
                oppositeWin[i] = -1;
            }
        }

        Log.d("xcm", "AI return " + p);
        return p;
    }

    @Nullable
    private Point algorithm2() {
        Point p;
        Point result = null;
        int max = 0;
        for (int i = 0; i < boardLine; i++) {
            for (int j = 0; j < boardLine; j++) {
                p = new Point(i, j);
                if (!mMyPositions.contains(p) && !mOppositePositions.contains(p)) {
                    int value = AIAlgorithm1.EvaluatePosition(p, mMyPositions, mOppositePositions);
                    if (value > max) {
                        max = value;
                        result = p;
                    }
                }
            }
        }
        mMyPositions.add(result);
        return result;
    }

    public Point algorithm3() {
        return null;
    }

    @Override
    public Data oneStep(Data inputData) {

        int type = inputData.getType();
        switch (type) {
            case Data.ServerAction.GAME_START:
                reset();
                if (inputData.getData().equals(Player.FIRST_SIDE + "")) {
                    Log.d(TAG, "oneStep: AI First");
                    // 1表示我方先走
                    Point firstPoint = getFirstPoint();
                    mMyPositions.add(firstPoint);
                    return new Data(Data.PlayerAction.PLAYER_DROP, JSONObject.toJSONString(firstPoint));
                }
                break;
            case Data.ServerAction.GAME_OVER:
                break;
            case Data.PlayerAction.PLAYER_DROP:
                return dealWithDrop(inputData.getData());
        }
        return null;
    }

    private Data dealWithDrop(String pointString) {
        Log.d("xcm", "dealWithDrop: " + pointString);
        Point p = JsonTools.toOject(pointString, Point.class);

        try {
            Thread.sleep(500);
        } catch (Exception e) {

        }

        mOppositePositions.add(p);
        for (int i = 0; i < winCount; i++) {
            if (allWins[p.x][p.y][i] == 1) {
                oppositeWin[i]++;
                myWin[i] = -1;
            }
        }
        switch (level) {
            case AI.LEVEL_EASY:
                p = algorithm1();
                break;
            case AI.LEVEL_MIDDLE:
                p = algorithm2();
                break;
            case AI.LEVEL_HARD:
                p = algorithm3();
                break;
        }

        Log.d(TAG, "AI return: " + p);
        if (p != null)
            return new Data(Data.PlayerAction.PLAYER_DROP, JsonTools.toJSONString(p));

        return null;
    }


    private Point getFirstPoint() {
        return new Point(boardLine / 2, boardLine / 2);
    }
}
